﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;
using HashFoo.Rest.Common;

namespace HashFoo.Rest.Server
{
    /// <summary>
    /// Utility functions and extensions ofr resource routes.
    /// </summary>
    public static class ResourceRouteUtil
    {
        /// <summary>
        /// Stubbed controller name so that our "controllerless" resource handlers don't break the MVC system.
        /// </summary>
        public const string ResourceControllerStubName = "_resource_controller_";

        /// <summary>
        /// Compile safe name lookup of the <see cref="IServiceHandler{TRequest}"/> handler method.
        /// </summary>
        static readonly Lazy<string> ResourceHandlerActionName = new Lazy<string>(() => GetMethodInfo((IServiceHandler<object> h) => h.Handle(null)).Name);

        static MemberInfo GetMemberInfo<T, TMemberExpression>(Expression<Func<T, TMemberExpression>> expression)
        {
            var member = expression.Body as MemberExpression;
            if (member != null)
                return member.Member;

            throw new ArgumentException("Expression is not a member access", "expression");
        }

        static MethodInfo GetMethodInfo<T, TMemberExpression>(Expression<Func<T, TMemberExpression>> expression)
        {
            var methodCall = expression.Body as MethodCallExpression;

            if (methodCall != null)
                return methodCall.Method;

            throw new ArgumentException("Expression is not a method call", "expression");
        }

        /// <summary>
        /// Registers MVC style routes for a set of <see cref="ResourceUri"/>s.
        /// </summary>
        /// <param name="routes"></param>
        /// <param name="resourceUriCollection"></param>
        public static void RegisterResourceRoutes(this RouteCollection routes, IResourceUriCollection resourceUriCollection)
        {
            foreach (var resourceUri in resourceUriCollection)
            {
                var genericHandlerType = typeof(IServiceHandler<>);
                var closedHandler = genericHandlerType.MakeGenericType(resourceUri.ResourceType);

                var resourceRouteDescriptor = new ResourceRouteDescriptor { HandlerType = closedHandler };
                var routeName = resourceUri.Uri.Replace("/", "_");

                routes.MapRoute(
                    routeName,
                    resourceUri.Uri,
                    new { resourceRouteDescriptor, action = ResourceHandlerActionName.Value, controller = ResourceControllerStubName });
            }
        }

        /// <summary>
        /// Gets the handler type from a request.  NULL if no <see cref="ResourceRouteDescriptor"/> could be found.
        /// </summary>
        /// <param name="requestContext"></param>
        /// <param name="controllerName"></param>
        /// <returns></returns>
        public static Type GetHandlerTypeFromRequest(this RequestContext requestContext, string controllerName)
        {
            var resourceRouteDescriptor = (ResourceRouteDescriptor)requestContext.RouteData.Values.Values.FirstOrDefault(x => x is ResourceRouteDescriptor);
            return resourceRouteDescriptor == null ? null : resourceRouteDescriptor.HandlerType;
        }
    }
}